/**
 * Title: jFamilyTree XSLT provider
 * Description: jFamilyTree provider using XSL-transformed file
 * Version: provider
 * Copyright: (c) 2001-2008
 * Terms of use:see license agreement at http://treebolic.sourceforge.net/en/license.htm
 * Author: Bernard Bou
 * Company: bsys
 */
package bsys.xml.transformer;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.net.URL;

import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.w3c.dom.Document;

/**
 * Transform (document to XML text)
 * 
 * @author Bernard Bou
 */
public class DomTransformer
{
	/**
	 * Output as html
	 */
	private boolean outputHtml;

	/**
	 * Dtd string
	 */
	private String theDtd;

	// C O N S T R U C T O R

	/**
	 * Constructor
	 */
	public DomTransformer()
	{
		this(false, null);
	}

	/**
	 * Constructor
	 * 
	 * @param thisOutputHtmlFlag
	 *        output as html
	 * @param thisDtd
	 *        dtd id
	 */
	public DomTransformer(boolean thisOutputHtmlFlag, String thisDtd)
	{
		outputHtml = thisOutputHtmlFlag;
		theDtd = thisDtd;
	}

	// T O . F I L E

	/**
	 * Transform XML file to XML file using XSLT file
	 * 
	 * @param thisInFilePath
	 *        in file
	 * @param thisOutFilePath
	 *        out file
	 * @param thisXsltFilePath
	 *        xslt file
	 * @throws TransformerException
	 * @throws IOException
	 */
	public void fileToFile(String thisInFilePath, String thisOutFilePath, String thisXsltFilePath) throws TransformerException, IOException
	{
		// xsl
		Source thisXslSource = new StreamSource(new File(thisXsltFilePath));

		// in
		Source thisSource =  thisInFilePath.equals("-") ? new StreamSource(System.in) : new StreamSource(new File(thisInFilePath));

		// out
		StreamResult thisResult = thisOutFilePath.equals("-") ? new StreamResult(System.out) : new StreamResult(new FileWriter(thisOutFilePath));

		// transform
		TransformerFactory thisTransformerFactory = TransformerFactory.newInstance();
		Transformer thisTransformer = thisTransformerFactory.newTransformer(thisXslSource);
		thisTransformer.transform(thisSource, thisResult);
	}

	/**
	 * Transform DOM document to XML file
	 * 
	 * @param thisDocument
	 *        is the DOM Document to be output as XML
	 * @param thisOutputFile
	 *        is the output file
	 * @throws TransformerConfigurationException
	 * @throws TransformerException
	 * @throws IOException
	 */
	public void documentToFile(Document thisDocument, File thisOutputFile) throws TransformerConfigurationException, TransformerException, IOException
	{
		toFile(thisDocument, null, thisOutputFile);
	}

	/**
	 * Transform DOM document to XML file after applying XSL transform
	 * 
	 * @param thisDocument
	 *        is the DOM Document to be output as XML
	 * @param thisXsltUrl
	 *        is the XSLT source file
	 * @param thisOutputFile
	 *        is the output file
	 * @throws TransformerConfigurationException
	 * @throws TransformerException
	 * @throws IOException
	 */
	public void documentToFile(Document thisDocument, URL thisXsltUrl, File thisOutputFile) throws TransformerConfigurationException, TransformerException, IOException
	{
		StreamSource thisXslt = new StreamSource(thisXsltUrl.openStream());
		toFile(thisDocument, thisXslt, thisOutputFile);
	}

	// T O . S T R I N G

	/**
	 * Transform DOM document to XML string
	 * 
	 * @param thisDocument
	 *        is the DOM Document to be output as XML
	 * @return XML String that represents DOM document
	 * @throws TransformerConfigurationException
	 * @throws TransformerException
	 * @throws IOException
	 */
	public String documentToString(Document thisDocument) throws TransformerConfigurationException, TransformerException, IOException
	{
		return toString(thisDocument, (Source) null);
	}

	/**
	 * Transform DOM document to XML string after applying XSL transformation
	 * 
	 * @param thisDocument
	 *        is the DOM Document to be output as XML
	 * @param thisXsltFile
	 *        is the XSLT source file
	 * @return XML String that represents DOM document
	 * @throws TransformerConfigurationException
	 * @throws TransformerException
	 * @throws IOException
	 */
	public String documentToString(Document thisDocument, File thisXsltFile) throws TransformerConfigurationException, TransformerException, IOException
	{
		StreamSource thisXslt = new StreamSource(thisXsltFile);
		return toString(thisDocument, thisXslt);
	}

	/**
	 * Transform DOM document to XML string after applying XSL transformation
	 * 
	 * @param thisDocument
	 *        is the DOM Document to be output as XML
	 * @param thisXsltUrl
	 *        is the XSLT source url
	 * @return XML String that represents DOM document
	 * @throws TransformerConfigurationException
	 * @throws TransformerException
	 * @throws IOException
	 */
	public String documentToString(Document thisDocument, URL thisXsltUrl) throws TransformerConfigurationException, TransformerException, IOException
	{
		StreamSource thisXslt = new StreamSource(thisXsltUrl.openStream());
		return toString(thisDocument, thisXslt);
	}

	// T O . S T R E A M

	/**
	 * Transform DOM document to XML stream
	 * 
	 * @param thisDocument
	 *        is the DOM Document to be output as XML
	 * @param thisOutputStream
	 *        is the output stream
	 * @throws TransformerConfigurationException
	 * @throws TransformerException
	 * @throws IOException
	 */
	public void documentToStream(Document thisDocument, OutputStream thisOutputStream) throws TransformerConfigurationException, TransformerException, IOException
	{
		StreamResult thisResult = new StreamResult(thisOutputStream);
		toStream(thisDocument, null, thisResult);
	}

	// T O . D O C U M E N T

	/**
	 * File to document
	 * 
	 * @param thisXmlUrl
	 *        XML document url
	 * @param thisXsltUrl
	 *        XSL url
	 * @return document
	 * @throws TransformerConfigurationException
	 * @throws TransformerException
	 * @throws IOException
	 */
	public Document fileToDocument(URL thisXmlUrl, URL thisXsltUrl) throws TransformerConfigurationException, TransformerException, IOException
	{
		StreamSource thisXml = new StreamSource(thisXmlUrl.openStream());
		StreamSource thisXslt = new StreamSource(thisXsltUrl.openStream());
		return toDocument(thisXml, thisXslt);
	}

	/**
	 * Transform DOM document to DOM document
	 * 
	 * @param thisDocument
	 *        DOM document
	 * @param thisXslt
	 *        XSLT url
	 * @return document
	 * @throws IOException
	 * @throws TransformerException
	 * @throws TransformerConfigurationException
	 */
	public Document documentToDocument(Document thisDocument, URL thisXslt) throws TransformerConfigurationException, TransformerException, IOException
	{
		DOMSource thisSource = new DOMSource(thisDocument);
		StreamSource thisStyleSource = new StreamSource(thisXslt.openStream());
		return toDocument(thisSource, thisStyleSource);
	}

	// H E L P E R S

	/**
	 * Transform DOM document to XML string
	 * 
	 * @param thisDocument
	 *        is the DOM Document to be output as XML
	 * @param thisXslt
	 *        is the XSLT source
	 * @return XML String that represents DOM document
	 * @throws TransformerConfigurationException
	 * @throws TransformerException
	 * @throws IOException
	 */
	protected String toString(Document thisDocument, Source thisXslt) throws TransformerConfigurationException, TransformerException, IOException
	{
		StringWriter thisWriter = new StringWriter();
		StreamResult thisResultStream = new StreamResult(thisWriter);
		toStream(thisDocument, thisXslt, thisResultStream);
		return thisWriter.toString();
	}

	/**
	 * Transform DOM document to XML file
	 * 
	 * @param thisDocument
	 *        is the DOM Document to be output as XML
	 * @param thisXslt
	 *        is the XSLT source
	 * @param thisFile
	 *        output file
	 * @throws TransformerConfigurationException
	 * @throws TransformerException
	 * @throws IOException
	 */
	protected void toFile(Document thisDocument, Source thisXslt, File thisFile) throws TransformerConfigurationException, TransformerException, IOException
	{
		StreamResult thisResultStream = new StreamResult(thisFile);
		toStream(thisDocument, thisXslt, thisResultStream);
	}

	/**
	 * Transform DOM document to stream result
	 * 
	 * @param thisDocument
	 *        is the DOM Document to be output as XML
	 * @param thisXslt
	 *        is the XSLT source
	 * @param thisResult
	 *        is the stream result
	 * @throws TransformerConfigurationException
	 * @throws TransformerException
	 * @throws IOException
	 */
	protected void toStream(Document thisDocument, Source thisXslt, StreamResult thisResult) throws TransformerConfigurationException, TransformerException, IOException
	{
		DOMSource thisSource = new DOMSource(thisDocument);

		// transform
		TransformerFactory thisTransformerFactory = TransformerFactory.newInstance();
		Transformer thisTransformer = thisXslt == null ? thisTransformerFactory.newTransformer() : thisTransformerFactory.newTransformer(thisXslt);
		thisTransformer.setOutputProperty(javax.xml.transform.OutputKeys.METHOD, outputHtml ? "html" : "xml");
		if (theDtd != null)
			thisTransformer.setOutputProperty(javax.xml.transform.OutputKeys.DOCTYPE_SYSTEM, theDtd);
		thisTransformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, "yes");
		thisTransformer.setOutputProperty(javax.xml.transform.OutputKeys.ENCODING, "UTF8");
		thisTransformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
		thisTransformer.transform(thisSource, thisResult);
	}

	/**
	 * Transform source to document
	 * 
	 * @param thisSource
	 *        the source
	 * @param thisXslt
	 *        the XSLT source
	 * @return document
	 * @throws TransformerConfigurationException
	 * @throws TransformerException
	 * @throws IOException
	 */
	protected Document toDocument(Source thisSource, Source thisXslt) throws TransformerConfigurationException, TransformerException, IOException
	{
		DOMResult thisResult = new DOMResult();

		// transform
		TransformerFactory thisTransformerFactory = TransformerFactory.newInstance();
		Transformer thisTransformer = thisXslt == null ? thisTransformerFactory.newTransformer() : thisTransformerFactory.newTransformer(thisXslt);
		thisTransformer.transform(thisSource, thisResult);
		return (Document) thisResult.getNode();
	}
}